package streaming;
import simulation.*;

import java.util.*;

public class SourceHost extends Host {
	
	int seqNo = 0;
	PeriodicPropagate periodicPropagate;
	
	// List of packets never requested by any peer - used to implement request overriding
	List neverRequested = new ArrayList();
	
	public SourceHost() {
		super(0);
		this.periodicRequest = null; // doesn't request because it is the source
		this.initializeWindows();
		this.periodicPropagate = new PeriodicPropagate(this);
	}
	
	public void propagate() {

		periodicPropagate.addEvent();
		
		//if ((seqNo % (10*StreamingConfig.MCASTRATE)) == 0)
			//System.out.println("propagating " + seqNo + " at " + Simulator.getInstance().getTime());
		notifyNeighbors(seqNo);
		
		if (Config.USE_COMPARATOR_MODULE){
			////notifyTracker(seqNo);
			//Simulator.getInstance().addIntDiagnosticResponseMessages();
		}
		
		neverRequested.add(new Integer(seqNo));
		seqNo++;
	}

	public void update() {
		super.update();
	    // Remove sequence numbers not within availability window
		if (!neverRequested.isEmpty()) {
			while (!availabilityWindow.isWithin( ((Integer)neverRequested.get(0)).intValue() )) {
				neverRequested.remove(0);
				
				if (neverRequested.isEmpty()) break;
			}
		}
	}
	
	public void handleRequest(int seqNo, Host source, boolean cm) {
		String data = "Chunk " + seqNo + " original.";
		
		Logs.printMessage("handleRequest", "[MSGf4] Peer " + this.getId() + " received request of chunk " + seqNo + " from peer " + source.getId());

		if (!availabilityWindow.isWithin(seqNo)) {
			return;
		}
		
		// Check bandwidth
		if (sentCount >= StreamingConfig.SENDERATTENDRATIO*StreamingConfig.MCASTRATE) {
			simulator.addEvent(simulator.getTime() + 0.2, new AttendRequest(this, seqNo, data, source, cm));
			
			return;
		}

		if (cm){
			new Message("packet", this, source, seqNo, data, cm);
			Logs.printMessage("handleRequest", "[MSGf5] Peer " + this.getId() + " sent chunk " + seqNo + " to peer " + source.getId() + ". DATA = [" + data + "].");
			sentCount++;
//			Simulator.getInstance().addIntDiagnosticResponseMessages();
			
			return;
		}
		
		// Request overriding
		if ((neverRequested.size() != 0) && (!neverRequested.contains(new Integer(seqNo)))) {
			int newSeqNo = ((Integer)neverRequested.get(0)).intValue();
			String newData = "Chunk " + newSeqNo + " original.";
			
			if (newSeqNo - availabilityWindow.getPacketStartSeqNo() > StreamingConfig.MCASTRATE) {
				new OverrideMessage(this, source, seqNo, newSeqNo, newData);
				neverRequested.remove(new Integer(newSeqNo));
				
				Logs.printMessage("handleRequest", "[MSGf5] Peer " + this.getId() + " sent chunk " + newSeqNo + " to peer " + source.getId() + ", overriding the original request of chunk " + seqNo +  ". DATA = [" + newData + "].");		
				
				return;
			}
		}
		new Message("packet", this, source, seqNo, data, cm);
		Logs.printMessage("handleRequest", "[MSGf5] Peer " + this.getId() + " sent chunk " + seqNo + " to peer " + source.getId() + ". DATA = [" + data + "].");
		neverRequested.remove(new Integer(seqNo));
		sentCount++;
//		Simulator.getInstance().addIntTotalChunksSent();
	}

	/**
	 * Had to override because the requestPacket method should not be called
	 */
	public void initializeWindows() {
		interestWindow = new Window(StreamingConfig.MCASTINTERESTWINDOWSIZE);
		availabilityWindow = new Window(StreamingConfig.MCASTWINDOWSIZE);
		this.update();
		this.notifyPackets();
		this.sendPacket();
	}
	
	public void removeNeighbor(Integer id){
		neighbors.remove(new Integer(id));
		neighborQueue.remove(overlay.getHost(id));
		outstandingNeighbors.remove(overlay.getHost(id));
	}
	
	/**
	 * 
	 * @param seqNo2
	 */
	private void notifyTracker(int seqNo) {
		new MessageToTracker("sourceChunkPropagation", seqNo);
	}
	
}